Esse tutorial te guiará no processo de criação de um gerenciamento centralizado de configurações para microservices usando Spring Cloud Config integrando com HashiCorp Vault.
Introduction
Atualmente é comum que softwares sejam entregues como serviço e não importa qual linguagem de programação foi escolhia, é sempre uma boa pratica seguir a metodologia dos doze fatores.
O primeiro fator fala sobre o código, ele começa dizendo:
One codebase tracked in revision control, many deploys
Se eu traduzir literalmente para português vai ficar bem estranho, mas a ideia é que o mesmo código deve ser deployado várias vezes em vários ambientes sem nenhuma mudança no código além das configurações, e isso nos leve para configurações externalizadas.
Se você está trabalhando com microservices em um ambiente elástico, você provavelmente notou a necessidade de um local centralizado para gerenciamento das configurações, e isso também é um dos doze fatores.
Pre-req
- JDK 1.8
- Editor de texto ou sua IDE favorita
- Maven 3.0+
- Docker
Spring Cloud Config
Spring Cloud Config provê server e client-side suporte para configurações externalizadas em sistemas distribuídos e ele tem alguns tipos de armazenamentos suportados, nesse tutorial nós vamos cobrir os seguintes:
Config Server
O jeito mais fácil de criar um Config Server é acessando start.spring.io, procure por Config Server
no campo de busca de dependências e click no botão Generate Project
, isso irá gerar um arquivo zip contendo o projeto com todas
as dependências necessárias. Unzip o projeto, abra a classe principal e adicione a anotação @EnableConfigServer
na classe.
Examplo:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
O projeto gerado contém um arquivo application.properties
localizado em src/main/resources
, por preferencias
pessoais eu renomeei esse arquivo para application.yml
.
Abra o arquivo application.yml
e adicione a seguinte configuração, como eu estou rodando todas as aplicações na
mesma máquina é importante alterar o server.port
.
server:
port: 8888
spring:
application:
name: configserver
Profiles & Implementações padrão
Spring Cloud Config Server usa profiles
para prover multiplas estratégias de armazenamento, vamos falar de algumas delas.
File System Backend
Existe um profile native
disponível onde o Config Server
procura por arquivos properties/YAML no classpath ou no file system.
spring:
profiles:
active: native
O valor padrão do searchLocations
é idêntico à uma aplicação Spring Boot ([classpath:/, classpath:/config, file:./, file:./config]
).
Isso não expõe o application.properties
do server para todos os clientes, porque qualquer property source presente no server é
removido antes de ser enviado para o cliente.
Você pode apontar para qualquer localização usando spring.cloud.config.server.native.searchLocations
.
Lembre-se de usar o prefixo file:
para arquivos no file system (o padrão sem prefixo é o classpath).
Como em qualquer configuração Spring Boot você pode usar placeholders ${}
para o ambiente, mas lembre-se
que caminhos absolutos no Windows requerem uma /
extra (por exemplo, file:///${user.home}/config-repo
).
Para esse tutorial eu criarei um diretório chamado config-repo
no ${user.home}
, esse novo diretório é
criado para armazenar os arquivos de configuração do profile native.
Agora adicione a seguinte configuração no application.yml
.
spring:
cloud:
config:
server:
native:
searchLocations: file://${user.home}/config-repo
Agora crie o arquivo config-repo/configclient.yml
, esse arquivo armazena a configuração para o nosso
microservice (será criado adiante nesse tutorial) chamado configclient
, e adicione a seguinte configuração:
client:
pseudo:
property: Property value loaded from File System
Agora você pode iniciar a aplicação e acessar o endpoint http://localhost:8888/configclient/default
, a resposta será:
{
"name":"configclient",
"profiles":[
"default"
],
"label":null,
"version":null,
"state":null,
"propertySources":[
{
"name":"file:///Users/my-user/config-repo/configclient.yml",
"source":{
"client.pseudo.property":"Property value loaded from File System"
}
}
]
}
O profile native
é um bom jeito de começar a usar o Config Server
em um ambiente local,
mas eu não recomendo que alguém use um armazenamento em file system para produção.
Git Backend
Também existe um git
profile onde você pode apontar um repositório git que contém todos os arquivos de configurações
para os seus microservices. Para fazer isso funcionar você pode adicionar o profile git
para os profiles ativos ou então
remover completamente a property spring.profiles.active
. Se você escolher manter essa propriedade, a última especificada
tem prioridade e sobreescreve o que está definido na anterior.
Adicione a seguinte configuração:
spring:
profiles:
active: native, git
cloud:
config:
server:
git:
uri: https://github.com/weekly-drafts/config-repo-spring-cloud-configserver-vault
Aqui a configuração completa para referencia:
spring:
profiles:
active: native, git
application:
name: configserver
cloud:
config:
server:
native:
searchLocations: file://${user.home}/config-repo
git:
uri: https://github.com/weekly-drafts/config-repo-spring-cloud-configserver-vault
Agora se você acessar http://localhost:8888/configclient/default
, o resultado será:
{
"name":"configclient",
"profiles":[
"default"
],
"label":null,
"version":null,
"state":null,
"propertySources":[
{
"name":"file:///Users/my-user/config-repo/configclient.yml",
"source":{
"client.pseudo.property":"Property value loaded from File System"
}
},
{
"name":"https://github.com/weekly-drafts/config-repo-spring-cloud-configserver-vault/configclient.yml",
"source":{
"client.pseudo.property":"Property value loaded from Git"
}
}
]
}
Se um profile não foi escolhido git
é o valor padrão.
Vault Backend
Sobre o Vault:
Vault é uma ferramenta para seguramente acessar secrets. Um secret é qualquer coisa que você queira ter controle sobre o acesso, como API keys, senhas, certificados e qualquer outra informação sensível. Valut provê uma interface unificada para qualquer secret enquanto fornece um log de autotoria detalhado e um controle de acesso.
Também há um profile vault
que habilita a integração com Vault para seguramente armazenar as
propriedades das aplicações, e nesse tópico nós usaremores docker
para criar o container do vault
.
Crie um container Vault:
docker run -d -p 8200:8200 --name vault -e 'VAULT_DEV_ROOT_TOKEN_ID=myroot' -e 'VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200' vault
Aqui está especificado um token root, não faça isso em produção.
Entre no container e autentique no Vault:
docker exec -i -t vault sh
export VAULT_ADDR='http://localhost:8200'
vault auth myroot
Agora é hora de escrevermos nossas propriedades:
vault write secret/configclient client.pseudo.property="Property value loaded from Vault"
Agora que temos tudo configurado é hora de configurar a comunicação entre o Config Server
e o Vault
, adicione
as seguintes propriedades:
spring:
profiles:
active: vault
cloud:
config:
server:
vault:
port: 8200
host: 127.0.01
Configuração completa para referência:
spring:
profiles:
active: native, git, vault
application:
name: configserver
cloud:
config:
server:
native:
searchLocations: file://${user.home}/config-repo
git:
uri: https://github.com/weekly-drafts/config-repo-spring-cloud-configserver-vault
vault:
port: 8200
host: 127.0.01
Você também pode armazenar as propriedades de uma maneira segura sem o Vault usando encryption.
Config Client
Como antes, o jeito mais fácil de criar um config client
é acessando start.spring.io
e adicionando Config Client
e Web
nas dependencias. Abra o arquivo gerado e crie o arquivo resources/bootstrap.yml
com o seguinte conteúdo:
spring:
application:
name: configclient
cloud:
config:
token: myroot
uri: http://localhost:8888
É importante adicionar exatamente configclient
como spring.application.name
porque esse é o nome usado para conectar com
as configurações externalizadas.
Agora abra a classe principal e adicione uma anotaçõa @Value
para recuperar nossa propriedade, aqui minha classe principal
para referência:
@RestController
@SpringBootApplication
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
@Value("${client.pseudo.property}")
private String pseudoProperty;
@GetMapping("/property")
public ResponseEntity<String> getProperty() {
return ResponseEntity.ok(pseudoProperty);
}
}
Depois de iniciar a aplicação será possível acessar http://localhost:8080/property
e o resultado esperado é:
Property value loaded from Vault
Sumário
Parabéns! Você acabou de criar um gerenciamento de configurações centralizado usando Spring Cloud Config e protegeu a senhas usando HashiCorp Vault.
Nota de rodapé
- O código usado nesse tutorial pode ser encontrado no GitHub
- Documentação do Spring Cloud Config